package com.ruoyi.utils;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.util.Base64Utils;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author zhouzhixiong
 * @Date 2024/3/16 9:38
 */
@Component
public class ProcessService {

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private HistoryService historyService;

    @Resource
    private TaskService taskService;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private ProcessEngine processEngine;

    /**
     * 启动特定类型的审批流程【发起一个实例】
     *
     * @param processDefinitionKey 流程定义Key，用于指定流程类型【比如单位注册、请假、OA、加班、】
     * @param businessKey          业务Key，用于关联业务数据
     * @param variables            流程变量，可以包含文件路径等信息
     */
    public String startProcess(String processDefinitionKey, String businessKey, Map<String, Object> variables) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
        return processInstance.getId();
    }

    /**
     * 根据流程实例ID获取当前待办的任务列表
     *
     * @param processInstanceId 流程实例ID
     */
    public List<Task> getActiveTasks(String processInstanceId) {
        return taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .active()
                .orderByTaskCreateTime().desc()
                .list();
    }

    /**
     * 根据流程实例ID和审批人获取待办任务的ID列表
     *
     * @param processInstanceId 流程实例ID
     * @param assignee 审批人
     */
    public List<String> getActiveTaskIdsByProcessInstanceIdAndAssignee(String processInstanceId, String assignee) {
        return taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .taskAssignee(assignee)
                .active()
                .list()
                .stream()
                .map(Task::getId)
                .collect(Collectors.toList());
    }

    /**
     * 根据流程实例ID和审批人获取已办理的任务ID列表
     *
     * @param processInstanceId 流程实例ID
     * @param assignee 审批人
     */
    public List<String> getCompletedTaskIdsByProcessInstanceIdAndAssignee(String processInstanceId, String assignee) {
        return historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .taskAssignee(assignee)
                .finished()
                .list()
                .stream()
                .map(HistoricTaskInstance::getId)
                .collect(Collectors.toList());
    }

    /**
     * 根据流程实例ID和审批人获取相关的所有任务ID列表
     *
     * @param processInstanceId 流程实例ID
     * @param assignee 审批人
     */
    public List<String> getAllTaskIdsByProcessInstanceIdAndAssignee(String processInstanceId, String assignee) {
        return historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .taskAssignee(assignee)
                .list()
                .stream()
                .map(HistoricTaskInstance::getId)
                .collect(Collectors.toList());
    }

    /**
     * 根据流程实例ID查询当前流程对应的待审批节点及其审批人并按照任务的创建时间降序排序
     *
     * @param processInstanceId 流程实例ID
     * @return 按照任务的创建时间降序排序的待审批节点及其审批人列表
     */
    public List<Map<String, String>> queryCurrentApprovalNodeAndAssignee(String processInstanceId) {
        return taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .active()
                .orderByTaskCreateTime().desc()
                .list()
                .stream()
                .map(task -> {
                    Map<String, String> taskDetails = new HashMap<>();
                    taskDetails.put("taskName", task.getName());
                    taskDetails.put("assignee", task.getAssignee());
                    taskDetails.put("createTime", task.getCreateTime().toString());
                    return taskDetails;
                })
                .collect(Collectors.toList());
    }

    /**
     * 根据任务ID查询当前审批节点在BPMN文件中的节点名字及其审批人
     *
     * @param taskId 任务ID
     */
    public Map<String, String> getNodeNameAndAssigneeByTaskId(String taskId) {
        Map<String, String> result = new HashMap<>();

        // 查询任务以获取流程定义ID和执行实例ID
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            return result; // 任务不存在时返回空结果
        }
        String assignee = task.getAssignee(); // 获取审批人
        String processDefinitionId = task.getProcessDefinitionId();

        // 获取BpmnModel
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        // 查询执行实例以获取当前活动ID
        Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
        if (execution != null) {
            String activityId = execution.getActivityId();

            // 从BpmnModel中获取当前活动节点的显示名称
            FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(activityId);
            if (flowElement != null) {
                result.put("nodeName", flowElement.getName());
                result.put("assignee", assignee);
            }
        }
        return result;
    }

    /**
     * 根据流程实例ID查询当前所有活动任务的节点名字、审批人及任务ID
     *
     * @param processInstanceId 流程实例ID
     * @return 包含所有任务详细信息的列表，每项包括节点名字、审批人和任务ID
     */
    public List<Map<String, String>> getNodeNameAssigneeAndTaskIdByProcessInstanceId(String processInstanceId) {
        List<Map<String, String>> taskDetailsList = new ArrayList<>();

        // 查询当前流程实例下的所有活动任务
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        if (tasks.isEmpty()) {
            return taskDetailsList; // 如果没有活动任务，返回空列表
        }

        // 获取流程定义ID
        String processDefinitionId = tasks.get(0).getProcessDefinitionId();

        // 获取BpmnModel
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        // 遍历所有任务
        for (Task task : tasks) {
            Map<String, String> taskDetails = new HashMap<>();
            String assignee = task.getAssignee(); // 获取审批人
            String taskId = task.getId(); // 获取任务ID

            // 查询执行实例以获取当前活动ID
            Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
            if (execution != null) {
                String activityId = execution.getActivityId();

                // 从BpmnModel中获取当前活动节点的显示名称
                FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(activityId);
                if (flowElement != null) {
                    taskDetails.put("nodeName", flowElement.getName());
                    taskDetails.put("assignee", assignee == null ? "未指派" : assignee);
                    taskDetails.put("taskId", taskId);
                    taskDetailsList.add(taskDetails);
                }
            }
        }

        return taskDetailsList;
    }


    /**
     * 根据任务ID完成任务
     *
     * @param taskId    任务ID
     * @param variables 流程变量，可用于传递审批结果等信息
     */
    public void completeTask(String taskId, Map<String, Object> variables) {
        taskService.complete(taskId, variables);
    }

    /**
     * 根据流程实例ID列表获取对应的业务键列表
     *
     * @param processInstanceIds 流程实例ID列表
     */
    public List<String> getBusinessKeysByProcessInstanceIds(List<String> processInstanceIds) {
        if (processInstanceIds == null || processInstanceIds.isEmpty()) {
            return Collections.emptyList();
        }

        return processInstanceIds.stream()
                .map(processInstanceId -> historyService.createHistoricProcessInstanceQuery()
                        .processInstanceId(processInstanceId)
                        .singleResult())
                .filter(Objects::nonNull)
                .distinct()
                .map(HistoricProcessInstance::getBusinessKey)
                .collect(Collectors.toList());
    }

    /**
     * 根据业务类型和businessKey获取最新任务节点的名称定义及审批人，不管流程是否结束
     *
     * @param processDefinitionKey 流程定义Key
     * @param businessKey 业务Key
     */
    public Map<String, String> getLatestTaskDetails(String processDefinitionKey, String businessKey) {
        // 查询最新的任务，不论已完成还是未完成，主要按照任务创建时间降序排列
        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
                .processDefinitionKey(processDefinitionKey)
                .processInstanceBusinessKey(businessKey)
                .orderByTaskCreateTime().desc()
                .list();

        if (tasks.isEmpty()) {
            return null;
        }

        // 取最新的一个任务
        HistoricTaskInstance latestTask = tasks.get(0);

        Map<String, String> taskDetails = new HashMap<>();
        taskDetails.put("nodeName", latestTask.getName());
        taskDetails.put("assignee", latestTask.getAssignee() == null ? "未指派" : latestTask.getAssignee());

        return taskDetails;
    }

    /**
     * 根据业务类型和businessKey获取所有活动任务节点的名称定义及审批人列表
     *
     * @param processDefinitionKey 流程定义Key
     * @param businessKey 业务Key
     * @return 任务详情列表
     */
    public List<Map<String, String>> getActiveTaskDetails(String processDefinitionKey, String businessKey) {
        // 查询所有活动的任务节点，主要按照任务创建时间降序排列
        List<Task> tasks = taskService.createTaskQuery()
                .processDefinitionKey(processDefinitionKey)
                .processInstanceBusinessKey(businessKey)
                .orderByTaskCreateTime().desc()
                .list();

        List<Map<String, String>> taskDetailsList = new ArrayList<>();

        // 遍历所有找到的任务节点
        for (Task task : tasks) {
            Map<String, String> taskDetails = new HashMap<>();
            taskDetails.put("nodeName", task.getName());
            taskDetails.put("assignee", task.getAssignee() == null ? "未指派" : task.getAssignee());
            taskDetailsList.add(taskDetails);
        }

        return taskDetailsList;
    }

    /**
     * 根据业务类型和BusinessKey获取待办任务
     * @param processDefinitionKey
     * @param businessKey
     * @return
     */
    public Map<String, String> getActiveTaskStringDetails(String processDefinitionKey, String businessKey) {
        // 查询所有活动的任务节点，主要按照任务创建时间降序排列
        List<Task> tasks = taskService.createTaskQuery()
                .processDefinitionKey(processDefinitionKey)
                .processInstanceBusinessKey(businessKey)
                .orderByTaskCreateTime().desc()
                .list();

        // 用于存储任务节点名称和审批人的字符串，初始化为空字符串
        String nodeNames = "";
        String assignees = "";

        // 遍历所有找到的任务节点
        for (Task task : tasks) {
            if (!nodeNames.isEmpty()) {
                // 不是第一个元素，添加逗号分隔符
                nodeNames += ",";
                assignees += ",";
            }
            // 添加任务节点名称和审批人
            nodeNames += task.getName();
            assignees += (task.getAssignee() == null ? "未指派" : task.getAssignee());
        }

        // 创建单个Map存储结果
        Map<String, String> taskDetails = new HashMap<>();
        taskDetails.put("nodeNames", nodeNames);
        taskDetails.put("assignees", assignees);

        return taskDetails;
    }

    /**
     * 检查指定流程实例ID的流程是否已经结束
     *
     * @param processInstanceId 流程实例ID
     * @return 如果流程已结束返回true，否则返回false
     */
    public boolean isProcessInstanceEnded(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        return historicProcessInstance != null && historicProcessInstance.getEndTime() != null;
    }

    /**
     * 终止一个指定的流程实例
     *
     * @param processInstanceId 要终止的流程实例ID
     */
    public void terminateProcessInstance(String processInstanceId) {
        if (runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult() != null) {
            runtimeService.deleteProcessInstance(processInstanceId, null);
        } else {
            throw new IllegalArgumentException("指定的流程实例ID不存在或者已经被删除");
        }
    }

    /**
     * 根据流程ID显示图像实例
     * @param processInstanceId
     * @return
     */
    public byte[] displayFlowCurrPic(String processInstanceId) {
        try {
            // 根据流程实例ID获取流程实例
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId).singleResult();
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());

            List<Task> activeTasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
            List<String> highLightedActivities = new ArrayList<>();
            for (Task task : activeTasks) {
                highLightedActivities.add(task.getTaskDefinitionKey());
            }

            // 使用 DefaultProcessDiagramGenerator 生成图像流
            InputStream imageStream = new DefaultProcessDiagramGenerator().generateDiagram(
                    bpmnModel, "png",
                    highLightedActivities, new ArrayList<>(),
                    "宋体", "宋体", "宋体", null, 1.0);

            // 将InputStream转换为byte[]
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = imageStream.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 判断当前活动节点的下一个节点是不是互斥网关并且名字叫做加签网关
     * @param processInstanceId 流程实例ID
     * @return boolean 表示下一个节点是否为名为“加签网关”的互斥网关
     */
    public boolean isNextNodeCounterSignatureGateway(String processInstanceId) {
        // 获取当前流程实例中最新的活动任务节点
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime().desc()
                .list();

        if (tasks.isEmpty()) {
            throw new IllegalArgumentException("Task for process instance with ID " + processInstanceId + " not found.");
        }

        // 取最新的一个任务
        Task currentTask = tasks.get(0);

        // 获取BPMN模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId());
        Process mainProcess = bpmnModel.getMainProcess();

        // 获取当前任务节点的流程元素
        FlowElement currentFlowElement = mainProcess.getFlowElement(currentTask.getTaskDefinitionKey(), true);

        // 检查后续节点
        if (currentFlowElement instanceof FlowNode) {
            FlowNode flowNode = (FlowNode) currentFlowElement;
            List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();

            if (outgoingFlows.isEmpty()) {
                return false; // 直接返回false如果没有外出流向
            }

            for (SequenceFlow sequenceFlow : outgoingFlows) {
                FlowElement nextFlowElement = sequenceFlow.getTargetFlowElement();
                if (nextFlowElement instanceof ExclusiveGateway && "加签网关".equals(nextFlowElement.getName())) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 驳回流程到上一个有效节点，并手动激活该节点。
     *
     * @param processInstanceId 流程实例ID
     * @param currentTaskId 当前任务ID
     */
    public void rollbackAndActivatePreviousValidTask(String processInstanceId, String currentTaskId) {
        Task currentTask = taskService.createTaskQuery().taskId(currentTaskId).singleResult();
        if (currentTask == null) {
            throw new IllegalArgumentException("当前任务不存在");
        }

        // 完成当前任务
        taskService.complete(currentTaskId);

        // 获取BPMN模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId());
        if (bpmnModel == null) {
            throw new IllegalArgumentException("无法获取BPMN模型");
        }

        List<HistoricActivityInstance> instances = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished()
                .orderByHistoricActivityInstanceEndTime().desc()
                .list();

        String targetActivityId = null;
        for (HistoricActivityInstance instance : instances) {
            FlowElement element = bpmnModel.getMainProcess().getFlowElement(instance.getActivityId());
            if (element != null && !element.getName().contains("加签")) {
                targetActivityId = instance.getActivityId();
                break;
            }
        }

        if (targetActivityId == null) {
            throw new IllegalArgumentException("未找到合适的驳回节点");
        }

        // 手动激活目标节点
        Map<String, Object> variables = runtimeService.getVariables(processInstanceId);
        runtimeService.startProcessInstanceByKey(currentTask.getProcessDefinitionId(), variables);
    }
}
